/***********************************************************************

	This file is part of KEEL-software, the Data Mining tool for regression, 
	classification, clustering, pattern mining and so on.

	Copyright (C) 2004-2010
	
	F. Herrera (herrera@decsai.ugr.es)
    L. Sánchez (luciano@uniovi.es)
    J. Alcalá-Fdez (jalcala@decsai.ugr.es)
    S. García (sglopez@ujaen.es)
    A. Fernández (alberto.fernandez@ujaen.es)
    J. Luengo (julianlm@decsai.ugr.es)

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see http://www.gnu.org/licenses/
  
**********************************************************************/

package keel.Algorithms.RE_SL_Methods.TSK_IRL;

import java.lang.Math;
import org.core.*;

class Est_mu_landa {

  public int n_gen_ee;
  public int Mu, Landa, n_sigma, n_alfa, n_total, nl_alfa, nm_alfa;
  public int Omega_x, Omega_sigma, Omega_alfa, Delta_x, Delta_sigma, Delta_alfa;
  public double Tau_0, Tau, Tau_1;
  public double[] Z;
  public int[] indices_seleccion;
  public int[] indices_recombinacion;
  public int[] ind_mayor;
  public Structure[] Padres;
  public Structure[] Hijos;
  private double PI = 3.1415926;

  public static double Beta = 0.0873;
  public static double Epsilon_sigma = 0.0001;
  /* parameter of the function "f" */
  public static int q = 5;
  /* Percentage of the maximum matching with which a example is considerated in the initial population */
  public static double porcentaje_h = 0.7;
  /* Individual Percentage of initial population generate with the 'a' values to 0 and a random 'b' value */
  public static double porcentaje_Mu = 0.2;
  /* Initial value of the sigmas */
  public static double Valor_Inicial_Sigma = 0.001;

  public BaseR base_reglas;
  public Adap fun_adap;
  public MiDataset tabla;

  public Est_mu_landa(BaseR base_r, Adap fun, MiDataset t, int gen_ee, int m,
                      int l, int n_sigm, int n_alf, int Omeg_x, int Omeg_sigma,
                      int Omeg_alfa, int Delt_x, int Delt_sigma, int Delt_alfa) {
    int i;

    base_reglas = base_r;
    fun_adap = fun;
    tabla = t;

    n_gen_ee = gen_ee;
    Mu = m;
    Landa = l;
    n_sigma = n_sigm;
    n_alfa = n_alf;
    Omega_x = Omeg_x;
    Omega_sigma = Omeg_sigma;
    Omega_alfa = Omeg_alfa;
    Delta_x = Delt_x;
    Delta_sigma = Delt_sigma;
    Delta_alfa = Delt_alfa;

    n_total = tabla.n_variables + n_sigma + n_alfa;
    Tau_0 = 1.0 / Math.sqrt(2 * tabla.n_variables);
    Tau = 1.0 / Math.sqrt(2 * Math.sqrt(tabla.n_variables));
    Tau_1 = 1.0 / Math.sqrt(tabla.n_variables);
    nl_alfa = tabla.n_variables + 1 - n_sigma;
    nm_alfa = tabla.n_variables - 1;

    Z = new double[tabla.n_variables];
    indices_seleccion = new int[Landa];
    indices_recombinacion = new int[ (int) Adap.Maximo(Delta_x, Adap.Maximo(Delta_sigma, Delta_alfa))];
    ind_mayor = new int[tabla.long_tabla];

    Padres = new Structure[Mu];
    Hijos = new Structure[Landa];

    for (i = 0; i < Mu; i++) {
      Padres[i] = new Structure(n_total);
    }
    for (i = 0; i < Landa; i++) {
      Hijos[i] = new Structure(n_total);
    }
  }

  /* -------------------------------------------------------------------------
                           Recombination Process
   ----------------------------------------------- -------------------------- */

  private double signo(double x) {
    if (x >= 0.0) {
      return (1);
    }
    else {
      return ( -1);
    }
  }

  /** Rounds the generated value by the semantics */
  double Asigna(double val, double tope) {
    if (val > -1E-4 && val < 1E-4) {
      return (0);
    }
    if (val > tope - 1E-4 && val < tope + 1E-4) {
      return (tope);
    }

    return (val);
  }

  /** Returns the 'a' values as little as we want */
  double f(double x, int y) {
    return (y * PI / 2 * Math.pow(x, q));
  }

  /** Generates the initial population of fathers */
  public void InicializaPadres() {
    int i, j, y, Mu_primer_grupo, pos_ep, total_mayor;
    double y_med, y_min, y_max, h_max, h_exigido, x;
    double imagen;

    /* we calculate the average, maximum and minimum high, and the matching with which a example is considerated in the initial population */
    y_med = y_min = y_max = tabla.datos[fun_adap.indices_ep[0]].ejemplo[tabla.
        n_var_estado];
    h_max = tabla.datos[fun_adap.indices_ep[0]].nivel_cubrimiento;

    for (i = 1; i < fun_adap.n_ejemplos_positivos; i++) {
      if (tabla.datos[fun_adap.indices_ep[i]].ejemplo[tabla.n_var_estado] > y_max) {
        y_max = tabla.datos[fun_adap.indices_ep[i]].ejemplo[tabla.n_var_estado];
      }
      if (tabla.datos[fun_adap.indices_ep[i]].ejemplo[tabla.n_var_estado] < y_min) {
        y_min = tabla.datos[fun_adap.indices_ep[i]].ejemplo[tabla.n_var_estado];
      }

      y_med += tabla.datos[fun_adap.indices_ep[i]].ejemplo[tabla.n_var_estado];
      if (tabla.datos[fun_adap.indices_ep[i]].nivel_cubrimiento > h_max) {
        h_max = tabla.datos[fun_adap.indices_ep[i]].nivel_cubrimiento;
      }
    }

    if (fun_adap.n_ejemplos_positivos > 0) {
      y_med /= fun_adap.n_ejemplos_positivos;
    }
    else {
      y_med = Double.MAX_VALUE;
    }
    h_exigido = porcentaje_h * h_max;

    /* Inicialization of a individual with 'b' value same as the average high and with the 'a' values to 0 */
    for (j = 0; j < tabla.n_var_estado; j++) {
      Padres[0].Gene[j] = 0;
    }
    Padres[0].Gene[tabla.n_var_estado] = Math.atan(y_med);

    /* Inicialization of the porcentaje_Mu * Mu individuals with 'b' value equal to a random value in the rank [y_min,y_max] and with the 'a' values to 0 */
    Mu_primer_grupo = (int) (porcentaje_Mu * Mu + 1);
    for (i = 1; i <= Mu_primer_grupo; i++) {
      for (j = 0; j < tabla.n_var_estado; j++) {
        Padres[i].Gene[j] = 0;
      }
      Padres[i].Gene[tabla.n_var_estado] = Math.atan(Randomize.Randdouble(y_min,
          y_max));
    }

    /* Inicialization of the remaining individuals with the random 'a' values and with a the 'b' value for any to example is in the plane */
    for (i = Mu_primer_grupo + 1; i < Mu; i++) {
      for (j = 0; j < tabla.n_var_estado; j++) {
        if (Randomize.Rand() < 0.5) {
          y = -1;
        }
        else {
          y = 1;
        }

        x = Randomize.Rand();
        Padres[i].Gene[j] = f(x, y);
      }

      /* we select randomly a example with a matching more high than "h_exigido" */
      for (total_mayor = pos_ep = 0; pos_ep < fun_adap.n_ejemplos_positivos;
           pos_ep++) {
        if (tabla.datos[fun_adap.indices_ep[pos_ep]].nivel_cubrimiento >= h_exigido) {
          ind_mayor[total_mayor++] = pos_ep;
        }
      }

      if (total_mayor == 0) {
        System.out.println("Error: The matching, with which a example is considerated in the initial population, isn't surmounted");
      }

      pos_ep = ind_mayor[Randomize.RandintClosed(0, total_mayor - 1)];
      for (imagen = 0.0, j = 0; j < tabla.n_var_estado; j++) {
        imagen += Math.tan(Padres[i].Gene[j]) *
            tabla.datos[fun_adap.indices_ep[pos_ep]].ejemplo[j];
      }

      Padres[i].Gene[tabla.n_var_estado] = Math.atan(tabla.datos[fun_adap.indices_ep[
          pos_ep]].ejemplo[tabla.n_var_estado] - imagen);
    }

    /* Inicialization of the vector of tipical desviations */
    for (i = 0; i < Mu; i++) {
      for (j = tabla.n_variables; j < tabla.n_variables + n_sigma; j++) {
        Padres[i].Gene[j] = Valor_Inicial_Sigma;
      }
    }

    /* Inicialization of the vector of angles: arcotangente of 1.0 */
    for (i = 0; i < Mu; i++) {
      for (j = tabla.n_variables + n_sigma;
           j < tabla.n_variables + n_sigma + n_alfa; j++) {
        Padres[i].Gene[j] = Math.atan(1.0);
      }
    }
  }

  /** Recombination Operators */
  private double OperadoresRecombinacion(int omega, int delta, int pos) {
    double u, suma, salida;
    int i, padre1, padre2;

    switch (omega) {
      /* Intermediate Global Recombination */
      case 1:
        for (suma = 0.0, i = 0; i < delta; i++) {
          suma += Padres[indices_recombinacion[i]].Gene[pos];
        }

        salida = suma / delta;
        break;

        /* Intermediate Local Recombination */
      case 2:
        padre1 = Randomize.RandintClosed(0, delta - 1);
        padre2 = Randomize.RandintClosed(0, delta - 1);
        u = Randomize.Rand();
        salida = u * Padres[indices_recombinacion[padre1]].Gene[pos] +
            (1 - u) * Padres[indices_recombinacion[padre2]].Gene[pos];
        break;

        /* Tactfully Recombination */
      default:
        padre1 = Randomize.RandintClosed(0, delta - 1);
        salida = Padres[indices_recombinacion[padre1]].Gene[pos];
        break;
    }

    return (salida);
  }

  /** Returns 1 if the current index is in the list of selected indexes */
  private int Pertenece(int[] indices_seleccionados, int ind_act,
                        int n_seleccionados) {
    int i;

    i = 0;
    while (i < n_seleccionados) {
      if (indices_seleccionados[i] == ind_act) {
        return (1);
      }
      else {
        i++;
      }
    }

    return (0);
  }

  /** Recombination process */
  private void Recombinacion() {
    int n_hijo, i, j, cadena, omega, delta, princ_cadena, fin_cadena, padre;

    for (n_hijo = 0; n_hijo < Landa; n_hijo++) {
      /* Recombination of the individual's parties:
             cadena=0 -> solution vector x.
             cadena=1 -> standard deviation vector sigma.
             cadena=2 -> vector of inclination angles alfa.
       */
      for (cadena = 0; cadena <= 2; cadena++) {
        switch (cadena) {
          case 0:
            omega = Omega_x;
            delta = Delta_x;
            princ_cadena = 0;
            fin_cadena = tabla.n_variables;
            break;

          case 1:
            omega = Omega_sigma;
            delta = Delta_sigma;
            princ_cadena = tabla.n_variables;
            fin_cadena = tabla.n_variables + n_sigma;
            break;

          default:
            omega = Omega_alfa;
            delta = Delta_alfa;
            princ_cadena = tabla.n_variables + n_sigma;
            fin_cadena = tabla.n_variables + n_sigma + n_alfa;
            break;
        }

        /* we perform if the string isn't empty */
        if (princ_cadena != fin_cadena) {
          /* we select the individual */

          /* we copy directly all if each individual is selected */
          if (delta == Mu) {
            for (i = 0; i < delta; i++) {
              indices_recombinacion[i] = i;
            }
          }
          /* we select randomly the individuals if all individuals aren't selected */
          else {
            for (i = 0; i < delta; i++) {
              do {
                indices_recombinacion[i] = Randomize.RandintClosed(0, delta - 1);
              }
              while (Pertenece(indices_recombinacion, indices_recombinacion[i],
                               i) == 1);
            }
          }

          if (omega == 0) {
            /* we select randomly a individual */
            padre = Randomize.RandintClosed(0, delta - 1);
            for (j = princ_cadena; j < fin_cadena; j++) {
              Hijos[n_hijo].Gene[j] = Padres[indices_recombinacion[padre]].Gene[
                  j];
            }
          }
          else {
            /* we recombine the current string with the selected operator omega */
            for (j = princ_cadena; j < fin_cadena; j++) {
              Hijos[n_hijo].Gene[j] = OperadoresRecombinacion(omega, delta, j);
            }
          }
        }
      }
    }
  }

  /* -------------------------------------------------------------------------
                          Mutation Process
   ------------------------------------------------------------------------- */

  /** Generates a normal value with hope 0 and tipical deviation "desv */
  private double ValorNormal(double desv) {
    double u1, u2;

    /* we generate 2 uniform values [0,1] */
    u1 = Randomize.Rand();
    u2 = Randomize.Rand();

    /* we calcules a normal value with the uniform values */
    return (desv * Math.sqrt( -2 * Math.log(u1)) * Math.sin(2 * PI * u2));
  }

  /* Mutation process of the generated son by means of the recombination */
  private void Mutacion() {
    int n_hijo, i, j, nq, n1, n2;
    double z0, z1, x1, x2, si, co;

    for (n_hijo = 0; n_hijo < Landa; n_hijo++) {
      /* Mutation of sigma */
      if (n_sigma == 1)
          /* if we use only a sigma, the sigma is adapted with Tau_1 */
           {
        Hijos[n_hijo].Gene[tabla.n_variables] *= ValorNormal(Tau_1);
      }
      else {
        z0 = ValorNormal(Tau_0);
        for (i = tabla.n_variables; i < tabla.n_variables + n_sigma; i++) {
          z1 = ValorNormal(Tau);
          Hijos[n_hijo].Gene[i] *= Math.exp(z1 + z0);

          /* The standard desviation is Epsilon_sigma if it becomes 0 */
          if (Hijos[n_hijo].Gene[i] == 0.0) {
            Hijos[n_hijo].Gene[i] = Epsilon_sigma;
          }
        }
      }

      /* Mutation of alfa */
      for (i = tabla.n_variables + n_sigma;
           i < tabla.n_variables + n_sigma + n_alfa; i++) {
        z0 = ValorNormal(Beta);
        Hijos[n_hijo].Gene[i] += z0;

        /* Si el valor mutado se sale del intervalo [-i,i], se proyecta
             circularmente el valor a dicho intervalo */
        if (Math.abs(Hijos[n_hijo].Gene[i]) > i) {
          Hijos[n_hijo].Gene[i] -= 2 * PI * signo(Hijos[n_hijo].Gene[i]);
        }
      }

      /* Mutation of x */

      /* we calculate the uncorrelated vector of mutations */
      for (i = 0; i < tabla.n_variables; i++) {
        if (tabla.n_variables + i < tabla.n_variables + n_sigma) {
          Z[i] = ValorNormal(Hijos[n_hijo].Gene[tabla.n_variables + i]);
        }
        else
        /* if there aren't more tipical desviations we use the latest */
        {
          Z[i] = ValorNormal(Hijos[n_hijo].Gene[tabla.n_variables + n_sigma - 1]);
        }
      }

      /* Correlation of the vector if we use the angles */
      if (n_alfa != 0) {
        nq = n_alfa;
        for (j = nl_alfa; j <= nm_alfa; ++j) {
          n1 = tabla.n_variables - j;
          n2 = tabla.n_variables;
          for (i = 1; i <= j; ++i) {
            x1 = Z[n1 - 1];
            x2 = Z[n2 - 1];
            si = Math.sin(Hijos[n_hijo].Gene[tabla.n_variables + n_sigma + nq -
                          1]);
            co = Math.cos(Hijos[n_hijo].Gene[tabla.n_variables + n_sigma + nq -
                          1]);
            Z[n2 - 1] = x1 * si + x2 * co;
            Z[n1 - 1] = x1 * co - x2 * si;
            --n2;
            --nq;
          }
        }
      }

      /* Final mutation of X */
      for (i = 0; i < tabla.n_variables; i++) {
        Hijos[n_hijo].Gene[i] += Z[i];

        if (Hijos[n_hijo].Gene[i] < - (PI / 2.0)) {
          Hijos[n_hijo].Gene[i] = - (PI / 2.0) + 1E-10;
        }
        if (Hijos[n_hijo].Gene[i] > (PI / 2.0)) {
          Hijos[n_hijo].Gene[i] = (PI / 2.0) - 1E-10;
        }
      }
    }
  }

  /* -------------------------------------------------------------------------
                      Selection Process
   ------------------------------------------------------------------------- */

  /** Selection process of the best Mu sons of the generated Landa sons */
  private void Seleccion() {
    int i, j, temp;

    /* we evaluate the Landa sons */
    for (i = 0; i < Landa; i++) {
      Hijos[i].Perf = fun_adap.eval_ml(Hijos[i].Gene);
    }

    /* we order the sons by mean of the bubble method */
    for (i = 0; i < Landa; i++) {
      indices_seleccion[i] = i;
    }

    for (i = 0; i < Landa; i++) {
      for (j = 0; j < Landa - i - 1; j++) {
        if (Hijos[indices_seleccion[j +
            1]].Perf < Hijos[indices_seleccion[j]].Perf) {
          temp = indices_seleccion[j];
          indices_seleccion[j] = indices_seleccion[j + 1];
          indices_seleccion[j + 1] = temp;
        }
      }
    }

    /* we select the best Mu sons */
    for (i = 0; i < Mu; i++) {
      for (j = 0; j < n_total; j++) {
        Padres[i].Gene[j] = Hijos[indices_seleccion[i]].Gene[j];
      }

      Padres[i].Perf = Hijos[indices_seleccion[i]].Perf;
    }
  }

  /* -------------------------------------------------------------------------
                        Evolution Strategy (Mu,Landa)
   ------------------------------------------------------------------------- */

  /** Main of the Evolution Strategy (Mu,Landa) */
  public void EE_Mu_Landa() {
    int i;

    InicializaPadres();
    for (i = 0; i < n_gen_ee; i++) {
      Recombinacion();
      Mutacion();
      Seleccion();

    }
  }

  public void guardar_solucion(Structure Padre) {
    int i, pos_individuo;

    pos_individuo = tabla.n_var_estado + 3 * tabla.n_var_estado;
    for (i = 0; i < tabla.n_var_estado; i++) {
      /* Valores 'a' del Consecuente */
      Padre.Gene[pos_individuo + i] = Padres[0].Gene[i];
    }
    /* Valor 'b' del Consecuente */
    Padre.Gene[pos_individuo + i] = Padres[0].Gene[tabla.n_var_estado];
  }

  public double[] solucion() {
    return (Padres[0].Gene);
  }

  public double fitness_solucion() {
    return (Padres[0].Perf);
  }
}

